<?php

namespace Muyuxuanpay\pay\logic;

use GuzzleHttp\Client;
use Muyuxuanpay\http\Http;
use Muyuxuanpay\pay\BasePay;
use Muyuxuanpay\tool\Mtool;

class ChangJie extends BasePay
{
    const URL = [
        'wx_pub' => 'https://pay.chanpay.com/ugtw/gateway',
        'merchant' => 'https://pay.chanpay.com/merchant/gateway',
        'alipay_wap' => 'https://ncount.hnapay.com/api/t013.htm',
        'wx_scan' => 'https://ncount.hnapay.com/api/r010.htm',
    ];
    const VERSION = '1.0';
    const RESULT_CODE_SUCCESS = '0000';

    /** @var string 交易等待中 */
    const RESULT_CODE_PINGAN = '9999';


    //供应商相关

    /**
     * 查询商户信息
     * @param $out_order_no
     * @return mixed
     * @throws \Exception
     */
    public function QueryMerchant($out_order_no){
        $now = date('YmdHis');
        $msgCiphertext = [
            'out_order_no'=>$out_order_no,
        ];
        $requestData = [
            'method'        => 'chanpay.merchant.create.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }
    public function CreateMerchant(array $data,$out_order_no){
        $now = date('YmdHis');
        $data['out_order_no'] = $out_order_no;
        $requestData = [
            'method'        => 'chanpay.merchant.normal.create',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($data, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }
    /**
     * 城市查询
     * @param array $data
     * @return mixed
     * @throws \Exception
     */
    public function QueryDistrict(array $data){
        $now = date('YmdHis');
        if(empty($data['city_code'])){
            throw new \Exception('城市不能为空');
        }
        $msgCiphertext = [
            'city_code'=>$data['city_code'],
        ];
        $requestData = [
            'method'        => 'chanpay.merchant.district.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }

    /**
     * 查询城市
     * @param array $data
     * @return mixed
     * @throws \Exception
     */
    public function QueryCity(array $data){
        $now = date('YmdHis');
        if(empty($data['province_code'])){
            throw new \Exception('省份不能为空');
        }
        $msgCiphertext = [
            'province_code'=>$data['province_code'],
        ];
        $requestData = [
            'method'        => 'chanpay.merchant.city.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }
    /**
     * 查询省份
     * @return mixed
     * @throws \Exception
     */
    public function QueryProvince(){
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchant.province.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody([], $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }
    /**
     * 查询银行支行
     * @param array $data
     * @return mixed
     * @throws \Exception
     */
    public function QueryCnapscodeBank(array $data){
        $now = date('YmdHis');
        if(empty($data['province_code'])){
            throw new \Exception('省份不能为空');
        }
        if(empty($data['city_code'])){
            throw new \Exception('城市不能为空');
        }
        $msgCiphertext = [
            'province_code'=>$data['province_code'],
            'city_code'=>$data['city_code'],
            'headbank_code'=>$data['headbank_code'],
            'headbank_name'=>$data['headbank_name'],
        ];
        $requestData = [
            'method'        => 'chanpay.merchant.cnapscode.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }
    /**
     * 查询总行
     * @return mixed
     * @throws \Exception
     */
    public function QueryHeadBank(){
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchant.headbank.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody([], $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }
    /**
     * 查询1级mcc
     * @return mixed
     * @throws \Exception
     */
    public function QueryMccOne(){
        $now = date('YmdHis');
//        $time_start = date('YmdHis',time()+10);
//        $time_expire = date('YmdHis',time()+1000);
        $requestData = [
            'method'        => 'chanpay.merchant.onelevelmcc.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody([], $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }

    /**
     * 查询2级mcc
     * @param array $data
     * @return mixed
     * @throws \Exception
     */
    public function QueryMccTwo(array $data){
        $now = date('YmdHis');
        if(empty($data['onelevelmcc_code'])){
            throw new \Exception('一级分类不能为空');
        }
        if(empty($data['onelevelmcc_name'])){
            throw new \Exception('一级分类名称不能为空');
        }
        $msgCiphertext = [
            'onelevelmcc_code'=>$data['onelevelmcc_code'],
            'onelevelmcc_name'=>$data['onelevelmcc_name'],
        ];
        $requestData = [
            'method'        => 'chanpay.merchant.twolevelmcc.query',
            'acq_sp_id'   => $this->config['acq_sp_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' =>$this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['merchant'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            if(isset($requestRus['sub_code'])&&$requestRus['sub_code']=='SUCCESS'){
                return $requestRus['content'];
            }else{
                throw new \Exception($requestRus['sub_msg']??'请求失败');
            }
        }else{
            throw new \Exception($requestRus['msg']??'请求失败');
        }
    }













    //商户相关

//    public function







    /**
     * 畅捷微信小程序支付
     * @param $totalPrice
     * @param $orderNo
     * @param $noticeUrl
     * @param $backUrl
     * @param $goodsDesc
     * @param $payTimeOut
     * @param array $event
     * @return array
     */
    public function wxmini($totalPrice,$orderNo,$noticeUrl="",$backUrl="",$goodsDesc='',$payTimeOut='',array $event=[]){
        return $this->mypay($totalPrice,'wxmini',$orderNo,$noticeUrl,$backUrl,$goodsDesc,$payTimeOut,$event);
    }

    /**
     * 畅捷微信H5支付
     * @param $totalPrice
     * @param $orderNo
     * @param $noticeUrl
     * @param $backUrl
     * @param $goodsDesc
     * @param $payTimeOut
     * @param $event
     * @return array
     */
    public function wxh5($totalPrice,$orderNo,$noticeUrl="",$backUrl="",$goodsDesc='',$payTimeOut='',$event=[]){
        return $this->mypay($totalPrice,'wxh5',$orderNo,$noticeUrl,$backUrl,$goodsDesc,$payTimeOut,$event);
    }
    /**
     * 畅捷总支付
     * @param $totalPrice
     * @param $orderNo
     * @param $noticeUrl
     * @param $backUrl
     * @param $goodsDesc
     * @param $payTimeOut
     * @param $event
     * @return array
     */
    public function mypay($totalPrice,$paymentType,$orderNo,$noticeUrl="",$backUrl="",$goodsDesc='',$payTimeOut='',$event=[]){
        $now = date('YmdHis');
        $time_start = date('YmdHis',time()+10);
        $time_expire = date('YmdHis',time()+1000);
        $msgCiphertext = [
            'out_trade_no' => $orderNo,
            'total_amount' => $totalPrice,
            'body'         => $goodsDesc,
            'mch_create_ip' =>Mtool::getIp(),
            'time_start'   => $time_start,
            'time_expire'  => $time_expire,
            'notify_url'   => $noticeUrl??$this->config['pay_notify'],
        ];

        $requestData = [
            'method'        => 'chanpay.merchanttrade.appletpay.cashier',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        $content = $requestRus['content'];
        if(in_array($paymentType,['wxmini','wxh5'])){
            $content['appId'] = "wx74793a591384fca6";
        }elseif(in_array($paymentType,['alipay','alipaymini'])){
            $content['appId'] = "2021004119683267";
        }
        if($paymentType=='wxmini'){
            $content['path'] = "pages/pay/pay?token={$content['token']}&appId={$this->config['app_id']}&path={$backUrl}";
        }elseif($paymentType=='wxh5'){
            $content['path'] = "https://cloud1-7gn9tve30c540732-1324444143.tcloudbaseapp.com/jump-mp.html?token={$content['token']}&appId={$this->config['app_id']}&path={$backUrl}";
        }elseif($paymentType=='alipay'){
            $content['path'] = "alipays://platformapi/startapp?appId=2021004119683267&page=pages/pay/pay".urlencode("?token={$content['token']}&path={$backUrl}");
        }elseif ($paymentType=='alipaymini'){
            $content['path'] = "alipays://platformapi/startapp?appId=2021004119683267&page=pages/pay/pay".urlencode("?token={$content['token']}&appId={$this->config['app_id']}&path={$backUrl}");
        }
        $resBack = [
            'pay_type'=>5,
            'resource'=>$content
        ];
        return $resBack;
    }
    /**
     * 查询订单
     * @param string|null $orderNo
     * @return array
     * @throws \Exception
     */
    public function QueryOrderNo(?string $orderNo){
        $msgCiphertext = [
            'out_trade_no'=>$orderNo,
        ];
        $now = date('YmdHis');
        //chanpay.merchanttrade.query
        $requestData = [
            'method'        => 'chanpay.merchanttrade.query',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            $content = $requestRus['content'];
            $status = 0;
            $message = "";
            if(isset($content['trade_state'])){
                switch ($content['trade_state']){
                    case 0:
                        $status = 1;
                        $message = "交易成功";
                        break;
                    case 1:
                        $status = 2;
                        $message = "已撤销";
                        break;
                    case 4:
                        $status = 4;
                        $message = '待支付状态';
                        break;
                    case 5:
                        $status = 5;
                        $message = '已关闭';
                        break;
                    case 6:
                        $status = 6;
                        $message = '部分退款';
                        break;
                    case 7:
                        $status = 7;
                        $message = '全部退款';
                        break;
                    case 8:
                        $status = 8;
                        $message = '转入退款';
                        break;
                    case 9:
                        $status = 9;
                        $message = '用户支付中';
                        break;
                    case 10:
                        $status = 10;
                        $message = '支付失败';
                        break;
                    case 11:
                        $status = 11;
                        $message = '交易失败';
                        break;
                    case 12:
                        $status = 12;
                        $message = '交易未知';
                        break;
                    default:
                        $message = '未知状态';
                }
            }
            return [
                'status'=>$status,
                'message'=>$message,
                'order_no'=>$orderNo,
                'resource'=>$content
            ];
        }else{
            throw new \Exception($requestRus['msg']??"请求失败");
        }
    }

    /**
     * 退款
     * @param string $orderNo 支付订单号
     * @param string $refundNo 退款订单号
     * @param string|int|float $allPrice
     * @param string|int|float $refundPrice
     * @param $noticeUrl
     * @param string $refundReason
     * @param array $event
     * @return false|mixed
     * @throws \Exception
     */
    public function RefundOrderNo(string $orderNo,string $refundNo,$allPrice,$refundPrice,$noticeUrl='',string $refundReason='',array $event=[]){
        $msgCiphertext = [
            'out_trade_no'=>$orderNo,
            'out_refund_no'=>$refundNo,
            'total_amount'=>intval(bcmul(strval($refundPrice),strval(100))),
            'refund_amount'=>intval(bcmul(strval($refundPrice),strval(100))),
        ];
        if(!empty($refundReason)){
            $msgCiphertext['refund_desc'] = $refundReason;
        }
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchanttrade.refund',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        if(isset($requestRus['code'])&&$requestRus['code']=='00000'){
            $content = $requestRus['content'];
            $status = 0;
            if(isset($content['refund_status'])){
                switch ($content['refund_status']){
                    case 0:
                        $status = 1;
                        $message = "交易成功";
                        break;
                    case 8:
                        $status = 8;
                        $message = "转入退款";
                        break;
                    case 11:
                        $status = 11;
                        $message = '交易失败';
                        break;
                    default:
                        $message = '未知状态';
                        $status = 12;
                        break;
                }
                return [
                    'status'=>$status,
                    'message'=>$message,
                    'order_no'=>$orderNo,
                    'resource'=>$content
                ];
            }
        }else{
            throw new \Exception($requestRus['msg']??"请求失败");
        }
        return $requestRus;
    }


    ///反扫相关
    ///

    /**
     * 扫码支付
     * @param string $orderNo 订单号
     * @param string $auth_code 扫码支付授权码
     * @param string|float|int $total_amount 支付金额(元为单位)
     * @param string $mch_create_ip 当前地址ip
     * @param string $body 商品描述
     * @param array $event 自定义参数 $event
     * @return array|false
     * @throws \Exception
     */
    public function reverseScanPay(string $orderNo,string $auth_code,$total_amount,$mch_create_ip=null,$body="",$event=[])
    {
        $zfb_zz = intval(substr($auth_code,0,2));
        $function_code = "";
        if($zfb_zz<20){
            $function_code = "1000100001";
        }elseif ($zfb_zz>=20&&$zfb_zz<=30){
            $function_code = "1000100002";
        }elseif ($zfb_zz>60){
            $function_code = "1000100003";
        }
        if(empty($function_code)){
            throw new \Exception("支付宝支付类型错误");
        }
        if(empty($mch_create_ip)){
            $mch_create_ip = Mtool::getIp();
        }
        $msgCiphertext = [
            'out_trade_no'=>$orderNo,
            'auth_code'=>$auth_code,
            'total_amount'=>intval($total_amount*100),
            'body'=>$body,
            'mch_create_ip'=>$mch_create_ip
        ];
        if(!empty($event)){
            $msgCiphertext = array_merge($msgCiphertext,$event);
        }
        $now = date('YmdHis');
        $method = "chanpay.merchanttrade.pay";
        $requestData = [
            'method'        => $method,
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $function_code,
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
//        echo "<pre>";
//        print_r($requestData);
//        exit;
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        $content = $requestRus['content'];
        if(isset($content['pay_result'])&&$content['pay_result']==0){
            $resBack = [
                'pay_type'=>6,
                'resource'=>$content
            ];
            return $resBack;
        }else{
            $this->errorMessage = $content['err_msg']??$requestRus['msg'];
            return false;
        }
    }

    /**
     * 查询反扫支付订单
     * @param $orderNo
     * @param $chanpay_trade_no
     * @return void
     * @throws \Exception
     */
    public function reverseQueryOrder($orderNo,$chanpay_trade_no=null)
    {
        $msgCiphertext = [
            'out_trade_no'=>$orderNo
        ];
        if(!empty($chanpay_trade_no)){
            $msgCiphertext['chanpay_trade_no'] = $chanpay_trade_no;
        }
        $now = date('YmdHis');
        $method = "chanpay.merchanttrade.query";
        $function_code = "1000311004";
        $requestData = [
            'method'        => $method,
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $function_code,
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        $content = $requestRus['content'];
        $status = 0;
        $message = "";
        switch ($content['trade_state']){
            case 0:
                $status = 1;
                $message = "支付成功";
                break;
            case 1:
                $status = 2;
                $message = "已撤销";
                break;
            case 4:
                $status = 0;
                $message = "支付中";
                break;
            case 5:
                $status = 5;
                $message = "已关闭";
                break;
            case 6:
                $status = 6;
                $message = "部分退款";
                break;
            case 7:
                $status = 7;
                $message = "全部退款";
                break;
            case 8:
                $status = 8;
                $message = "转入退款";
                break;
            case 9:
                $status = 0;
                $message = "支付中";
                break;
            case 10:
                $status = 10;
                $message = "支付失败";
                break;
            case 11:
                $status = 11;
                $message = "支付失败";
                break;
            case 12:
                $status = 12;
                $message = "支付失败";
                break;
            default:
                $status = 0;
                $message = "支付中";
                break;
        }
        return ['status'=>$status,'message'=>$message,'resource'=>$content];
    }

    /**
     * 取消反扫订单
     * @param $orderNo
     * @param $chanpay_trade_no
     * @return array
     * @throws \Exception
     */
    public function reverseCancelOrder($orderNo,$chanpay_trade_no=null)
    {
        $msgCiphertext = [
            'out_trade_no'=>$orderNo
        ];
        if(!empty($chanpay_trade_no)){
            $msgCiphertext['chanpay_trade_no'] = $chanpay_trade_no;
        }
        $now = date('YmdHis');
        $method = "chanpay.merchanttrade.cancel";
        $function_code = "1000311004";
        $requestData = [
            'method'        => $method,
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $function_code,
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        $content = $requestRus['content'];
        $status = 0;
        $message = "";
        switch ($content['trade_state']){
            case 0:
                $status = 1;
                $message = "取消成功";
                break;
            case 11:
                $status = 11;
                $message = "取消失败";
                break;
            default:
                $status = 11;
                $message = "取消失败";
                break;
        }
        return ['status'=>$status,'message'=>$message,'resource'=>$content];
    }

    /**
     * 反扫订单退款
     * @param string $out_trade_no 订付订单号
     * @param string $out_refund_no 退款订单号
     * @param string|int|float $total_amount 总金额
     * @param string|int|float $refund_amount 退款金额
     * @param string $auth_code 支付付款码信息
     * @param array $event
     * @return void
     */
    public function reverseRefundOrder($out_trade_no,$out_refund_no,$total_amount,$refund_amount,$auth_code,$event=[])
    {
        $zfb_zz = intval(substr($auth_code,0,2));
        $function_code = "";
        if($zfb_zz<20){
            $function_code = "1000100001";
        }elseif ($zfb_zz>=20&&$zfb_zz<=30){
            $function_code = "1000100002";
        }elseif ($zfb_zz>60){
            $function_code = "1000100003";
        }
        if(empty($function_code)){
            throw new \Exception("支付宝支付类型错误");
        }
        $msgCiphertext = [
            'out_trade_no'=>$out_trade_no,
            'out_refund_no'=>$out_refund_no,
            'total_amount'=>intval($total_amount*100),
            'refund_amount'=>intval($refund_amount*100),
        ];
        if(!empty($event)){
            $msgCiphertext = array_merge($msgCiphertext,$event);
        }
        $now = date('YmdHis');
        $method = "chanpay.merchanttrade.refund";
        $requestData = [
            'method'        => $method,
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $function_code,
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
        ];
        $requestData['biz_content']   = $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']);
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        $content = $requestRus['content'];
        $status = 0;
        switch ($content['refund_status']){
            case 0:
                $status = 1;
                $message = "交易成功";
                break;
            case 8:
                $status = 8;
                $message = "转入退款";
                break;
            case 11:
                $status = 11;
                $message = "交易失败";
                break;
            default:
                $status = 11;
                $message = "交易失败";
                break;
        }
        return ['status'=>$status,'message'=>$message,'resource'=>$content];
    }

    /**
     * 退款订单查询
     * @param $out_trade_no
     * @param $out_refund_no
     * @param $event
     * @return array
     * @throws \Exception
     */
    public function reverseRefundQueryOrder($out_trade_no,$out_refund_no,$event=[])
    {
        $msgCiphertext = [
            'out_trade_no'=>$out_trade_no,
            'out_refund_no'=>$out_refund_no,
        ];
        if(!empty($event)){
            $msgCiphertext = array_merge($msgCiphertext,$event);
        }
        $now = date('YmdHis');
        $method = "chanpay.merchanttrade.refundquery";
        $function_code = "1000311004";
        $requestData = [
            'method'        => $method,
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $function_code,
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
        ];
        $requestData['biz_content']   = $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']);
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        $content = $requestRus['content'];
        $status = 0;
        switch ($content['refund_status']){
            case 0:
                $status = 1;
                $message = "交易成功";
                break;
            case 6:
                $status = 6;
                $message = "部分退款";
                break;
            case 7:
                $status = 7;
                $message = "全部退款";
                break;
            case 8:
                $status = 8;
                $message = "转入退款";
                break;
            case 11:
                $status = 11;
                $message = "交易失败";
                break;
            default:
                $status = 11;
                $message = "交易失败";
                break;
        }
        return ['status'=>$status,'message'=>$message,'resource'=>$content];
    }

    ///分账相关




    /**
     * 绑定分账关系
     * @param array $data
     * @param string|null $out_trade_no
     * @return void
     */
    public function BindSeparateAccounts(array $data,?string $out_trade_no){
        if(empty($out_trade_no)){
            throw new \Exception("缺少订单号");
        }
        $msgCiphertext = [
            'out_trade_no'=>$out_trade_no,
        ];
        $msgCiphertext = array_merge($msgCiphertext,$data);
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchant.profitsharing.relation.bind',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        return $requestRus;
    }

    /**
     * 解绑分账关系
     * @param array $data
     * @param string|null $out_trade_no
     * @return void
     */
    public function UnbindSeparateAccounts(array $data,?string $out_trade_no){
        if(empty($out_trade_no)){
            throw new \Exception("缺少订单号");
        }
        $msgCiphertext = [
            'out_order_no'=>$out_trade_no,
        ];
        $msgCiphertext = array_merge($msgCiphertext,$data);
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchant.profitsharing.relation.unbind',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        return $requestRus;
    }

    /**
     * 查询分账关系
     * @param string|null $out_order_no
     * @return void
     */
    public function QueryBindSeparateAccounts(?string $out_order_no){
        if(empty($out_trade_no)){
            throw new \Exception("缺少订单号");
        }
        $msgCiphertext = [
            'out_order_no'=>$out_order_no,
        ];
//        $msgCiphertext = array_merge($msgCiphertext,$data);
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchant.profitsharing.relation.query',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        return $requestRus;
    }

    /**
     * 分账申请
     * @param $out_trade_no
     * @param array $data
     * @param string|null $out_order_no
     * @return mixed
     * @throws \Exception
     */
    public function Profitsharing($out_trade_no,array $data,?string $out_order_no){
        if(empty($out_order_no)){
            $out_order_no = $this->getOrderNo();
        }
        $msgCiphertext = [
            'out_order_no'=>$out_order_no,
            'out_trade_no'=>$out_trade_no,
        ];
        $msgCiphertext = array_merge($msgCiphertext,$data);
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchanttrade.profitsharing',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        return $requestRus;
    }
    /**
     * 完成分账
     * @param string|null $orderNo
     * @return mixed
     * @throws \Exception
     */
    public function CompleteDivide(?string $orderNo){
        if(empty($orderNo)){
            throw new \Exception('订单号不能为空');
        }
        $msgCiphertext = [
            'out_trade_no'=>$orderNo
        ];
        $now = date('YmdHis');
        $requestData = [
            'method'        => 'chanpay.merchanttrade.profitsharing.finish',
            'merchant_id'   => $this->config['merchant_id'],
            'product_code'  => $this->config['product_code'],
            'function_code' => $this->config['function_code'],
            'format'        => 'JSON',
            'charset'       => 'UTF-8',
            'sign_type'     => 'RSA2',
            'version'       => self::VERSION,
            'timestamp'     => $now,
            'biz_content'   => $this->getEncryptionRequestBody($msgCiphertext, $this->config['public_key']),
        ];
        $requestData['sign'] = $this->getSign($requestData, $this->config['private_key']);
        $requestRus = $this->post(self::URL['wx_pub'], $requestData);
        return $requestRus;
    }















    //sdk相关
    protected function getEncryptionRequestBody(array $data, string $certificate):string{
        $certificate = sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", $certificate);

        $publicKey = openssl_get_publickey($certificate);
        if ($publicKey === false) {
            throw new \Exception('解析密钥失败');
        }
        //证书详情
        if(empty($data)){
            $jsonData = "{}";
        }else{
            $jsonData = json_encode($data);
        }
        //分割字符串加密
        $encrypt = '';
        foreach (str_split($jsonData, 117) as $str) {
            if (!openssl_public_encrypt($str, $encryptItem, $publicKey)) {
                throw new \Exception("报文签名失败");
            }
            $encrypt .= $encryptItem;
        }

        return base64_encode($encrypt);
    }
    /**
     * 新帐通报文签名
     * @param array $data
     * @param string $certificate
     * @return string
     * @throws \Exception
     */
    protected function getSign(array $data,$certificate=''): string
    {
        if(empty($certificate)){
            $certificate = $this->config['private_key'];
        }
        $certificate = sprintf("-----BEGIN RSA PRIVATE KEY-----\n%s\n-----END RSA PRIVATE KEY-----", wordwrap($certificate, 64, "\n", true));
        $encrypt = '';
        $signStr = $this->toSignStr($data);

        $privateKey = openssl_get_privatekey($certificate);

        if ($privateKey === false) {
            throw new \Exception('解析密钥失败');
        }
        if (!openssl_sign($signStr, $encrypt, $privateKey, "SHA256")) {
            throw new \Exception('报文签名失败');
        }
        return base64_encode($encrypt);
    }

    //biz_content加密参数按格式组装
    protected function toSignStr($values)
    {
        $buff = '';
        $buff .= 'method' . '=' . $values['method'] . '&';
        if(isset($values['merchant_id'])){
            $buff .= 'merchant_id' . '=' . $values['merchant_id'] . '&';
        }
        if(isset($values['acq_sp_id'])){
            $buff .= 'acq_sp_id' . '=' . $values['acq_sp_id'] . '&';
        }
        if(isset($values['function_code'])){
            $buff .= 'function_code' . '=' . $values['function_code'] . '&';
        }
        if(isset($values['product_code'])){
            $buff .= 'product_code' . '=' . $values['product_code'] . '&';
        }
//        $buff .= 'product_code' . '=' . $values['product_code'] . '&';
        $buff .= 'biz_content' . '=' . $values['biz_content'];
//        echo "<pre>";
//        print_r($buff);
//        exit;
        return trim($buff, '&');
    }
    //RSA2全局返参解密
    public function rsaDecrypt($content,$certificate=''){
        if(empty($certificate)){
            $certificate = $this->config['private_key'];
        }
        $certificate = sprintf("-----BEGIN RSA PRIVATE KEY-----\n%s\n-----END RSA PRIVATE KEY-----", wordwrap($certificate, 64, "\n", true));
        $encrypt = '';

        $privateKey = openssl_get_privatekey($certificate);

        if ($privateKey === false) {
            throw new \Exception('解析密钥失败');
        }

        $decrypted = '';
        foreach (str_split(base64_decode($content), 256) as $chunk) {
            if (!openssl_private_decrypt($chunk, $decryptData, $privateKey, OPENSSL_PKCS1_PADDING)) {
                throw new \Exception("报文解密失败");
            }
            $decrypted .= $decryptData;
        }

        return $decrypted;
    }
    //解密码时把转换后的符号替换特殊符号
    protected function urlsafe_b64decode($string) {
        $data = str_replace(array('-','_'),array('+','/'),$string);
        $mod4 = strlen($data) % 4;
        if ($mod4) {
            $data .= substr('====', $mod4);
        }
        return base64_decode($data);
    }

    protected function post(string $url, $data)
    {
        $obj = new Client();
        $response = $obj->post($url, [
            'form_params' => $data,
        ]);
        if ($response->getStatusCode() != 200) {
            throw new \Exception('请求失败,服务器返回状态码:' . $response->getStatusCode());
        }
        $responseContent = $response->getBody()->getContents();
        if(empty($responseContent)){
            throw new \Exception('请求失败,服务器返回状态码:' . $response->getStatusCode());
        }
        $responseContent = json_decode($responseContent, true);
        if($responseContent==''||$responseContent==null){
            throw new \Exception('请求失败,服务器返回状态码:' . $response->getStatusCode());
        }
        if($responseContent['code']!='00000'){
            throw new \Exception($responseContent['msg']??"无返回消息");
        }
        if($responseContent['sub_msg'] =='FAIL' || empty($responseContent['content'])){
            throw new \Exception($responseContent['sub_msg']??"无返回消息");
        }
        $resultDecrypt = $this->rsaDecrypt($responseContent['content'], $this->config['private_key']);
        if(empty($resultDecrypt)){
            throw new \Exception('解密失败');
        }
        $responseContent['content'] = json_decode($resultDecrypt, true);
        if(is_string($responseContent['content'])){
            $responseContent['content'] = json_decode($responseContent['content'], true);
        }
        return $responseContent;
    }

    protected function generateOrderNo(): string
    {
        return md5(uniqid('M') . substr(microtime(true), -3));
    }
    public function BinUser(?array $data,?string $orderNo=null){
        if(empty($data)){
            throw new \Exception('请传入参数');
        }
        if(count($data)>10){
            throw new \Exception('参数不能大于10个');
        }
    }
}